package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.dao.SkuInfoMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuInfoService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class SkuInfoServiceImpl implements SkuInfoService {
    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public void importSkuInfoToEs() {

        List<Sku> skuList = skuFeign.findSkusByStatus("1").getData();
        //先转成字符串 然后再转成对应的list   也可以用先遍历 然后用BeanUtils一个一个转
        String skuListJson = JSON.toJSONString(skuList);
        List<SkuInfo> skuInfos = JSON.parseArray(skuListJson, SkuInfo.class);
        // 处理动态字段
        if (skuInfos != null && skuInfos.size() > 0) {
            for (SkuInfo skuInfo : skuInfos) {
                String spec = skuInfo.getSpec();
                Map<String, Object> map = JSON.parseObject(spec, Map.class);
                skuInfo.setSpecMap(map);
            }
            // 将数据导入es中
            skuInfoMapper.saveAll(skuInfos);
        }


    }


    /**
     * 检索数据
     *
     * @param searchMap 搜索条件
     * @return
     */
    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {
        // 1.构建检索条件(后期有多个条件检索，因此我们专门封装一个方法)
        NativeSearchQueryBuilder builder = builderBasicQuery(searchMap);
        // 2.根据关键字搜索列表
        Map<String, Object> resultMap = searchList(builder);
        //3.组合查询
        Long totalElements = (Long) resultMap.get("totalElements");
        Map<String, Object> groupMap = searchByGroupList(builder, totalElements.intValue());

        resultMap.putAll(groupMap);

        NativeSearchQuery build = builder.build();
        Pageable pageable = build.getPageable();

        int pageNumber = pageable.getPageNumber();
        int pageSize = pageable.getPageSize();

        resultMap.put("pageNum",pageNumber+1);//当前页
        resultMap.put("pageSize",pageSize);//页面大小


        return resultMap;

    }

    /**
     * 组合查询   品牌+分类+规格
     *
     * @param builder
     * @param totalElements
     * @return
     */
    private Map<String, Object> searchByGroupList(NativeSearchQueryBuilder builder, Integer totalElements) {


        //品牌
        builder.addAggregation(AggregationBuilders.terms("sku_brand").field("brandName").size(totalElements));
        builder.addAggregation(AggregationBuilders.terms("sku_category").field("categoryName").size(totalElements));
        builder.addAggregation(AggregationBuilders.terms("sku_spec").field("spec.keyword").size(totalElements));


        //执行搜索
        AggregatedPage<SkuInfo> result = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        //获取聚合结果
        Aggregations aggregations = result.getAggregations();
        List<String> brandList = getGroupResult(aggregations, "sku_brand");
        List<String> categoryList = getGroupResult(aggregations, "sku_category");
        List<String> specs = getGroupResult(aggregations, "sku_spec");
        Map<String, Set<String>> specList = specPutAll(specs);


        Map<String, Object> map = new HashMap<>();//封装结果集
        map.put("brandList", brandList);
        map.put("categoryList", categoryList);
        map.put("specList", specList);
        return map;


    }

    //处理聚合结果
    private List<String> getGroupResult(Aggregations aggregations, String searchName) {

        //获取分组结果
        StringTerms terms = aggregations.get(searchName);
        List<String> list = new ArrayList<>();

        // List<String> list = terms.getBuckets().stream().map(b -> b.getKeyAsString()).collect(Collectors.toList());
        //返回品牌名称
        List<StringTerms.Bucket> buckets = terms.getBuckets();
        for (StringTerms.Bucket bucket : buckets) {
            String keyAsString = bucket.getKeyAsString();
            list.add(keyAsString);
        }

        return list;
    }


    //处理规格数据
    private Map<String, Set<String>> specPutAll(List<String> specList) {

        Map<String, Set<String>> specMap = new HashMap<>();
        if (specList != null && specList.size() > 0) {
            for (String specString : specList) {
                Map<String, String> map = JSON.parseObject(specString, Map.class);
                Set<String> keys = map.keySet(); //key值
                if (keys != null && keys.size() > 0) {

                    for (String key : keys) {
                        //key 规格名字
                        String value = map.get(key);//规格选项值
                        //判断 specMap里是否有这个规格了
                        Set<String> specValues = specMap.get(key);
                        if (specValues == null) {
                            specValues = new HashSet<String>();
                        }
                        specValues.add(value);
                        specMap.put(key, specValues);

                    }

                }


            }
        }

        return specMap;
    }


    //搜索列表数据  + 高亮
    private Map<String, Object> searchList(NativeSearchQueryBuilder builder) {

        //设置高亮显示的条件
        HighlightBuilder.Field field = new HighlightBuilder.Field("name");
        field.preTags("<font color='red'>"); //高亮字段前缀
        field.postTags("</font>");  //高亮字段后缀
        builder.withHighlightFields(field); //添加高亮条件

        SearchResultMapper searchResultMapper = new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                //结果集处理
                SearchHits hits = searchResponse.getHits();
                //封装结果集
                List<SkuInfo> content = new ArrayList<>();
                if (hits!=null && hits.totalHits>0){
                    for (SearchHit hit : hits) {

                        String text = hit.getSourceAsString(); // json格式的字符串
                        SkuInfo skuInfo = JSON.parseObject(text, SkuInfo.class);//普通结果
                        HighlightField highlightField = hit.getHighlightFields().get("name");
                        if (highlightField!=null){
                            Text[] fragments = highlightField.getFragments();
                            String highLightName = fragments[0].toString();
                            //将普通字段替换成高亮的名称
                            skuInfo.setName(highLightName);

                        }
                        content.add(skuInfo);

                    }
                }

                return new AggregatedPageImpl<>((List<T>) content,pageable,hits.getTotalHits());
            }
        };

        NativeSearchQuery build = builder.build();
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(build, SkuInfo.class,searchResultMapper);
        List<SkuInfo> rows = page.getContent();//list结果集
        int totalPages = page.getTotalPages();//总页数
        long totalElements = page.getTotalElements();//总条数
        //封装结果集
        Map<String, Object> map = new HashMap<>();
        map.put("rows", rows);
        map.put("totalPages", totalPages);
        map.put("totalElements", totalElements);

        return map;
    }


    //搜索列表数据
 /*   private Map<String, Object> searchList(NativeSearchQueryBuilder builder) {

        NativeSearchQuery build = builder.build();
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(build, SkuInfo.class);
        List<SkuInfo> rows = page.getContent();//list结果集
        int totalPages = page.getTotalPages();//总页数
        long totalElements = page.getTotalElements();//总条数
        //封装结果集
        Map<String, Object> map = new HashMap<>();
        map.put("rows", rows);
        map.put("totalPages", totalPages);
        map.put("totalElements", totalElements);

        return map;
    }*/


    //封装查询条件

    /**
     * 构建基本查询
     *
     * @param searchMap
     * @return
     */
    private NativeSearchQueryBuilder builderBasicQuery(Map<String, String> searchMap) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        //构建布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //当前页
        String pageNum =  "";
        //当前页面大小
        String pageSize = "";

        if (searchMap != null ) {
            //(1)获取关键字
            String keywords = searchMap.get("keywords");
            if (!StringUtils.isEmpty(keywords)) {
                nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));
            }
            //(2)品牌   termQuery
            //不带分析器，比如说你搜索“中国”，没有分析器你就搜索不到，而
            //matchQuery
            //就带了分析器，你搜索“中国”的时候他会自动使用自带的中文分析器帮你去检索，那么你就能搜索到关于“中国”的信息。
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                boolQueryBuilder.must(QueryBuilders.matchQuery("brandName", searchMap.get("brand")));
            }
            //(3)分类搜索
            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                boolQueryBuilder.must(QueryBuilders.matchQuery("categoryName", searchMap.get("category")));
            }
            //(4)规格
            for (String key : searchMap.keySet()) {
                //如果是规格参数
                if (key.startsWith("spec_")) {
                    boolQueryBuilder.must(QueryBuilders.matchQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));
                }
            }
            //价格区间

            String price = searchMap.get("price");
            if (!StringUtils.isEmpty(price)){
                String[] priceArray = price.split("-");
                //【min - max】
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(priceArray[0]));
                if (priceArray.length==2){

                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(priceArray[1]));

                }

            }


            //(6)价格 ，销量  新品或者 其他排序 排序
            //排序字段
            String sortField = searchMap.get("sortField");
            //排序规则
            String sortRule = searchMap.get("sortRule");
            if (!StringUtils.isEmpty(sortRule)) {
                if ("ASC".equalsIgnoreCase(sortRule)) {
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(SortOrder.ASC));
                } else {
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(SortOrder.DESC));
                }
            }
            //添加过滤
            nativeSearchQueryBuilder.withFilter(boolQueryBuilder);

             pageNum = searchMap.get("pageNum");
             pageSize = searchMap.get("pageSize");
        }



        //6.分页


        int page = 1;
        int size = 40;
        if (!StringUtils.isEmpty(pageNum)) {

            page = Integer.valueOf(pageNum);
        }

        if (!StringUtils.isEmpty(pageSize)) {
            size = Integer.valueOf(pageSize);
        }
        Pageable pageable = PageRequest.of(page - 1, size);
        nativeSearchQueryBuilder.withPageable(pageable);




        return nativeSearchQueryBuilder;
    }
}
